﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Collections.Specialized;

namespace ArGe_Tcp
{   //Kodlayan : Said AYDOĞAN
    //Anlatan  : Said AYDOĞAN
    public partial class ClientForm : Form
    {
        public ClientForm()
        {
            InitializeComponent();
            ServerMesajYolladi += new dgServerMesajGeldi(ClientForm_ServerMesajYolladi);
            ServerBaglantiKoptu += new dgServerBaglantiKoptu(ClientForm_ServerBaglantiKoptu);
            ServerKontrolTimer = new Timer();
            ServerKontrolTimer.Tick += new EventHandler(ServerKontrolTimer_Tick);
        }


        /// <summary>
        /// Serverdan gelecek mesajları sürekli bekler.
        /// </summary>
        public delegate void ServerDinleyiciDelegate();

        /// <summary>
        /// Serverdan yeni mesaj geldiğinde tetiklelecek delate ve eventi.
        /// </summary>
        /// <param name="SMevent">Gelen Mesaj Parametrelerini içerir.</param>
        public delegate void dgServerMesajGeldi(ServerMesajEvent SMevent);
        public event dgServerMesajGeldi ServerMesajYolladi;

        /// <summary>
        /// Server a erişilemediğinde tetiklenecek delegate ve eventi.
        /// </summary>
        public delegate void dgServerBaglantiKoptu();
        public event dgServerBaglantiKoptu ServerBaglantiKoptu;

        void BilgiYaz(string BilgiMesaji)
        {
            txtMesajAlani.Text += BilgiMesaji + "\n";
        }

        void ServerKontrolTimer_Tick(object sender, EventArgs e)
        {
            try
            {
                byte[] ba = System.Text.Encoding.UTF8.GetBytes("komut=YasamTesti");
                strmGonderici = client.GetStream();
                strmGonderici.Write(ba, 0, ba.Length);
                strmGonderici.Flush();
            }
            catch
            {
                ServerBaglantiKoptu();
            }
        }







        //[Server programında anlatıldı]
        NameValueCollection ServerMesajiniAyristir(string SMesaj)
        {
            try
            {
                string[] parametreler = SMesaj.Split('&');
                NameValueCollection nvcParametreler = new NameValueCollection(parametreler.Length);
                foreach (string parametre in parametreler)
                {
                    string[] esitlik = parametre.Split('=');
                    nvcParametreler.Add(esitlik[0], esitlik[1]);
                }
                return nvcParametreler;
            }
            catch (Exception)
            {
                return null;
            }
        }


        void AracDurumlari(bool Bagli)
        {
            if (Bagli)
            {
                txtIP.Enabled = false;
                txtPort.Enabled = false;
                txtNick.Enabled = false;
                btnBaglan.Enabled = false;
                btnGonder.Enabled = true;
                btnKapat.Enabled = true;
            }
            else
            {
                txtIP.Enabled = true;
                txtPort.Enabled = true;
                txtNick.Enabled = true;
                btnBaglan.Enabled = true;
                btnGonder.Enabled = false;
                btnKapat.Enabled = false;
            }

        }

        private void ClientForm_Load(object sender, EventArgs e)
        {
            //Çapraz iş parçacıklarının (threadların) nesnelere erişiminin denetlenmesini devre dışı ediyoruz
            CheckForIllegalCrossThreadCalls = false;
            //Anlatıma btnBaglan_Click den devam ediyoruz..
        }
        TcpClient client;
        Timer ServerKontrolTimer;
        private void btnBaglan_Click(object sender, EventArgs e)
        {
            try
            {
                BilgiYaz("Sunucuya Bağlanılıyor...");
                //TcpClient nesnemizle ip ve port bilgilerini kullanarak server ' a bağlanıyoruz..
                client = new TcpClient();
                client.Connect(txtIP.Text, int.Parse(txtPort.Text));

                //Sürekli serverdan gelecek mesajları dinleyen delagatemizi asenkron olarak çalıştırıyoruz.
                ServerDinleyiciDelegate dinleyici = new ServerDinleyiciDelegate(ServerDinle);
                dinleyici.BeginInvoke(null, null);

                //Server ı her 30 saniyede bir kontrol edecek ve bağlı olup olmadığını denetleyecek timer i başlatıyoruz.
                ServerKontrolTimer.Interval = 30000;
                ServerKontrolTimer.Start();

                //Server a giris komutu gönderilip araçların  gerekli hallerini almasını sağlıyoruz.
                MesajYolla("komut=Giris&isim=" + txtNick.Text);
                lblDurum.ForeColor = System.Drawing.Color.Green;
                lblDurum.Text = "Bağlandı";
                BilgiYaz("Sunucuya \"" + txtNick.Text + "\" İsmiyle Giriş Yapıldı.");
                AracDurumlari(true);
                btnGonder.Enabled = false;
            }
            catch
            {
                BilgiYaz("Sunucuya Bağlanılamadı. Ip ve Port u kontrol ediniz.");
                lblDurum.Text = "Bağlanamadı";
            }
            //Anlatıma ServerDinle den devam ediyoruz.
        }
        public void ServerDinle()
        {
            NetworkStream strm;
            //Client in sunucuya bağlandığında sürekli mesajı dinlemesini sağlıyoruz..
            while (client.Connected)
            {
                try
                {
                    //Veri kaynaklarını alıyoruz.
                    strm = client.GetStream();
                    //Gelen veri kullanılabilirse devam ediyoruz.
                    //Sürekli dinlemede olduğu için mesaj gelmedi anlarda 0 bit veri gelmekte bu durumd gereksiz yere mesaj incelemesi yaptırmıyoruz.                
                    if (strm.DataAvailable)
                    {
                        //Veri okunuyor ve mesaj haline getirilip ServerMesajYolladi eventi tetikleniyor.
                        string Mesaj = "";
                        byte[] serverGelenVeri = new byte[client.Available];
                        strm.Read(serverGelenVeri, 0, client.Available);

                        char[] gelenKarakterDizisi = System.Text.Encoding.UTF8.GetChars(serverGelenVeri);

                        for (int i = 0; i < gelenKarakterDizisi.Length; i++)
                        {
                            Mesaj += gelenKarakterDizisi[i].ToString();
                        }
                        ServerMesajYolladi(new ServerMesajEvent(Mesaj));
                        strm.Flush();
                        //Anlatıma ServerMesajiInceleme 'den devam ediyoruz..
                    }
                }
                catch
                {
                    //Veri kaynaklarına ulaşılamaz ya da veri okunamazsa bağlantıyı kesiyoruz.
                    client.Close();
                    ServerBaglantiKoptu();
                    break;
                }
            }
        }
        void ClientForm_ServerMesajYolladi(ServerMesajEvent SMevent)
        {
            Invoke(new dgServerMesajGeldi(ServerMesajiInceleme), SMevent);
        }
        void ServerMesajiInceleme(ServerMesajEvent SMevent)
        {
            //Server programında bu kısmı teferruatıyla anlattığımızdan geçiyoruz..
            //Sadece bazı komutları anlatacağız.
            NameValueCollection ServerAyrintiliMesaj = ServerMesajiniAyristir(SMevent.Mesaj);
            if (ServerAyrintiliMesaj == null || ServerAyrintiliMesaj.Count < 1)
            {
                return;
            }
            try
            {
                switch (ServerAyrintiliMesaj["komut"])
                {
                    //[Gayet Anlaşılır]
                    case "listele":
                        KullaniciListele(ServerAyrintiliMesaj["isimler"]);
                        break;
                    //[MesajGeldi Metodunu da geçiyoruz.]
                    case "GelenMesaj":
                        MesajGeldi(ServerAyrintiliMesaj["Kimden"], ServerAyrintiliMesaj["Mesaj"]);
                        break;
                    //[ClientBaglantisiKoptu Metodunu da geçiyoruz.]
                    case "BaglantiKoptu":
                        ClientBaglantisiKoptu(int.Parse(ServerAyrintiliMesaj["Kim"]));
                        break;
                    //Aynı nickte kullanıcı varsa bağlantıyı kesiyoruz.
                    case "IsimDegis":
                        ServerBaglantiKoptu(); MessageBox.Show("Aynı nickle sunucuda kullanıcı bulunuyor.");
                        break;
                    //Anlatıma BaglantiKoptu dan devam ediyoruz.  
                    case "ServerDurdu":
                        ServerBaglantiKoptu();
                        break;
                }
            }
            catch
            {

            }
        }
        private void KullaniciListele(string ClientAdlar)
        {
            //Gelen Mesaja göre clienleri listeye ekliyoruz..
            btnGonder.Enabled = true;
            lstKullanicilar.Items.Clear();
            string[] isimler = ClientAdlar.Split(',');
            for (int i = 0; i < isimler.Length; i++)
            {
                if (isimler[i] != "")
                    lstKullanicilar.Items.Add(isimler[i]);
            }
        }
        private void MesajGeldi(string Kimden, string Mesaj)
        {
            BilgiYaz(Kimden + " ==>  " + Mesaj);
        }
        private void ClientBaglantisiKoptu(int ID)
        {
            BilgiYaz("(" + lstKullanicilar.Items[ID].ToString() + ") Kullanıcısı Sunucudan Ayrıldı.");
            lstKullanicilar.Items.RemoveAt(ID);
        }
        void ClientForm_ServerBaglantiKoptu()
        {
            Invoke(new dgServerBaglantiKoptu(BaglantiKoptu));
        }
        void BaglantiKoptu()
        {
            //Client bağlantısı kesiliyor ve kullanıcı bilgilendiriliyor.
            //Ayrıca server ı kontrol eden timer da durduruluyor.
            client.Client.Close();
            BilgiYaz("Sunucuya Erişilemiyor..!");
            lblDurum.ForeColor = System.Drawing.Color.Red;
            lblDurum.Text = "Bağlantı Koptu";
            ServerKontrolTimer.Stop();
            AracDurumlari(false);
            //btnKapat_Click den devam ediyoruz.
        }
        private void btnKapat_Click(object sender, EventArgs e)
        {
            //Server a cikis komutu gönderilir ve bilgilendirilir. Bağlantı kesilir.
            MesajYolla("komut=Cikis&Kim=" + txtNick.Text);
            BilgiYaz("Sunucudan Ayrıldınız..");
            client.Close();
            AracDurumlari(false);
            //MesajYolla dan devam ediyoruz.
        }
        NetworkStream strmGonderici;
        private void btnGonder_Click(object sender, EventArgs e)
        {
            MesajYolla("komut=MesajYolla&Kime=" + lstKullanicilar.SelectedIndex + "&Mesaj=" + txtMesaj.Text);
            BilgiYaz(txtNick.Text + " ==>  " + txtMesaj.Text);
        }
        void MesajYolla(string Mesaj)
        {
            //Gönderilmek istenen mesaj bit haline getilir ve TcpClient nesnesinin veri kaynaklarına yazılır.
            try
            {
                System.Text.Encoding encoding = System.Text.Encoding.UTF8;

                byte[] ba = System.Text.Encoding.UTF8.GetBytes(Mesaj);
                strmGonderici = client.GetStream();
                strmGonderici.Write(ba, 0, ba.Length);
                strmGonderici.Flush();
            }
            catch
            {
                BilgiYaz("Mesaj Gönderilemedi..");
            }

        }

    }
    //Delegatelerde kullanılan event
    public class ServerMesajEvent : EventArgs
    {
        public string Mesaj
        {
            get { return mesaj; }
            set { mesaj = value; }
        }
        private string mesaj;

        public ServerMesajEvent(string mesaj)
        {
            this.mesaj = mesaj;
        }
    }
}
